package com.zhangjingcheng.yunyixia.service.impl;

import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zhangjingcheng.yunyixia.constant.FileConstant;
import com.zhangjingcheng.yunyixia.constant.MessageConstant;
import com.zhangjingcheng.yunyixia.constant.StatusConstant;
import com.zhangjingcheng.yunyixia.constant.UserConstant;
import com.zhangjingcheng.yunyixia.em.LoginType;
import com.zhangjingcheng.yunyixia.exception.FileBusinessException;
import com.zhangjingcheng.yunyixia.exception.UserBusinessException;
import com.zhangjingcheng.yunyixia.mapper.UserMapper;
import com.zhangjingcheng.yunyixia.pojo.dto.*;
import com.zhangjingcheng.yunyixia.pojo.entity.User;
import com.zhangjingcheng.yunyixia.pojo.vo.AllUserVO;
import com.zhangjingcheng.yunyixia.pojo.vo.UserLoginVO;
import com.zhangjingcheng.yunyixia.pojo.vo.UserSpaceVO;
import com.zhangjingcheng.yunyixia.result.PageResult;
import com.zhangjingcheng.yunyixia.service.UserService;
import com.zhangjingcheng.yunyixia.util.*;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.ModelAndView;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@Slf4j
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private UserMapper userMapper;

    @Value("${qqLogin.url}")
    private String url;

    @Value("${admin.code}")
    private String adminCode;

    @Value("${qqLogin.redirect}")
    private String redirectUrl;

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private S3UploadUtil s3UploadUtil;


    /**
     * 根据邮箱查询用户
     *
     * @param email
     * @return
     */
    @Override
    public User findUserByEmail(String email) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<User>()
                .eq(User::getEmail, email);
        // 执行查询
        return userMapper.selectOne(queryWrapper);
    }

    /**
     * 根据昵称查询用户
     *
     * @param nickName
     * @return
     */
    @Override
    public User findUserByNickName(String nickName) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<User>()
                .eq(User::getNickName, nickName);
        return userMapper.selectOne(queryWrapper);
    }

    /**
     * 根据QQOpenId查询用户
     *
     * @param qqOpenId
     * @return
     */
    @Override
    public User findUserByQqOpenId(String qqOpenId) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<User>()
                .eq(User::getQqOpenId, qqOpenId);
        return userMapper.selectOne(queryWrapper);
    }

    /**
     * 注册用户
     *
     * @param dto
     */
    @Override
    public void register(UserRegisterDTO dto) {
        // 验证码校验
        checkCode(dto.getEmail() + UserConstant.CHECK_EMAIL_CODE, dto.getCode());
        // 密码校验
        String password = dto.getPassword();
        if (password.length() < 6 || password.length() > 16) {
            throw new UserBusinessException(MessageConstant.PASSWORD_ERROR);
        }
        // 用户名校验
        User user = findUserByEmail(dto.getEmail());
        if (user != null) {
            throw new UserBusinessException(MessageConstant.ALREADY_EXISTS);
        }
        // 昵称检验
        user = findUserByNickName(dto.getNickName());
        if (user != null) {
            throw new UserBusinessException(MessageConstant.NICKNAME_EXISTS);
        }
        user = new User();
        // 保存用户
        if (dto.getAdminCode().equals(adminCode)) {
            user.setAdmin(StatusConstant.ENABLE);
        }
        BeanUtils.copyProperties(dto, user, "password");
        user.setPassword(StringUtil.encodeByMD5(password));
        user.setRegisterTime(LocalDateTime.now());
        user.setStatus(StatusConstant.ENABLE);
        user.setUsedSpace(FileConstant.USED_SPACE);
        user.setTotalSpace(FileConstant.INIT_SPACE);
        userMapper.insert(user);
    }

    /**
     * 用户登录
     *
     * @param dto
     * @return
     */
    @Override
    public UserLoginVO login(UserLoginDTO dto) {
        // 验证码校验
        checkCode(dto.getCheckCode(), dto.getCheckCode());

        // 用户校验和密码检验
        User user = findUserByEmail(dto.getEmail());
        if (user == null || !StringUtil.encodeByMD5(dto.getPassword()).equals(user.getPassword())) {
            throw new UserBusinessException(MessageConstant.EMAIL_OR_PASSWORD_ERROR);
        }
        // 执行登录操作
        return (UserLoginVO) doLogin(user, LoginType.NORMAL.name());
    }

    /**
     * 重置密码
     *
     * @param dto
     * @return
     */
    @Override
    public void resetPwd(UserLoginDTO dto) {
        log.info("重置密码：{}", dto);
        // 校验验证码
        checkCode(dto.getEmail() + UserConstant.CHECK_EMAIL_CODE, dto.getCheckCode());
        // 查询检验用户
        User user = findUserByEmail(dto.getEmail());
        if (user == null) {
            throw new UserBusinessException(MessageConstant.ACCOUNT_NOT_FOUND);
        }
        // 重置密码
        User newUser = User.builder()
                .id(user.getId())
                .password(StringUtil.encodeByMD5(dto.getPassword()))
                .build();
        userMapper.updateUser(newUser);
    }

    /**
     * 获取用户空间信息
     *
     * @return
     */
    @Override
    public UserSpaceVO getUserSpace() {
        User user = userMapper.selectById(ThreadLocalUtil.getId());
        return new UserSpaceVO(user.getUsedSpace(), user.getTotalSpace());
    }

    /**
     * 退出登录
     */
    @Override
    public void logout(String token) {
        redisUtil.deleteCache(token);
    }

    /**
     * 获取用户头像
     *
     * @return
     */
    @Override
    public String getUserAvatar() {
        User user = userMapper.selectById(ThreadLocalUtil.getId());
        return user.getQqAvatar();
    }

    /**
     * 更新用户头像
     *
     * @param avatarUrl
     */
    @Override
    public void updateAvatar(String avatarUrl) {
        User oldUser = userMapper.selectById(ThreadLocalUtil.getId());
        if (oldUser.getQqAvatar() != null) {
            s3UploadUtil.delete(oldUser.getQqAvatar());
        }
        User user = User.builder()
                .id(ThreadLocalUtil.getId())
                .qqAvatar(avatarUrl)
                .build();
        userMapper.updateUser(user);
    }

    /**
     * 更新用户密码
     *
     * @param dto
     */
    @Override
    public void updatePwd(UpdatePwdDTO dto) {
        User user = userMapper.selectById(ThreadLocalUtil.getId());
        if (!StringUtil.encodeByMD5(dto.getOldPwd()).equals(user.getPassword())) {
            throw new UserBusinessException(MessageConstant.PASSWORD_ERROR);
        }
        User newUser = User.builder()
                .id(user.getId())
                .password(StringUtil.encodeByMD5(dto.getNewPwd()))
                .build();
        userMapper.updateUser(newUser);
    }

    /**
     * QQ登录
     *
     * @param code
     * @return
     */
    @Override
    public ModelAndView qqLogin(String code, HttpServletResponse response, HttpSession session) {
        // 获取QQ登录信息
        Map<String, Object> params = new HashMap<>();
        params.put("code", code);
        try {
            // 发送请求
            JSONObject qqResponse = HttpClientUtil.doGet(url, params);
            log.info("QQ登录响应：{}", qqResponse);
            // 获取用户信息
            String nickName = (String) qqResponse.get("nickname");
            String qqAvatar = (String) qqResponse.get("figureurl_qq_2");
            String qqOpenId = (String) qqResponse.get("open_id");
            log.info("获取到的QQ用户信息：nickName={}, qqAvatar={}, qqOpenId={}", nickName, qqAvatar, qqOpenId);
            // 判断用户是否用QQ登录过
            User userByQqOpenId = findUserByQqOpenId(qqOpenId);
            if (userByQqOpenId != null) {
                log.info("用户已通过QQ登录过：{}", userByQqOpenId);
                // 执行登录操作
                session.setAttribute(String.valueOf(StatusConstant.DISABLE), userByQqOpenId.getId());
                return (ModelAndView) doLogin(userByQqOpenId, LoginType.QQ.name());
            }
            // 用户未注册就自动注册
            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<User>()
                    .eq(User::getNickName, nickName);
            Long sameNickName = userMapper.selectCount(queryWrapper);
            // 判断昵称是否已被使用，如果昵称已被使用，则生成新的昵称
            if (sameNickName != 0L) {
                sameNickName = sameNickName + 1L;
                nickName = nickName + sameNickName + StringUtil.getRandomString(UserConstant.CODE_LENGTH);
            }
            // 注册用户
            User newUser = User.builder()
                    .nickName(nickName)
                    .qqOpenId(qqOpenId)
                    .password(StringUtil.encodeByMD5(qqOpenId))
                    .qqAvatar(qqAvatar)
                    .status(StatusConstant.ENABLE)
                    .usedSpace(FileConstant.USED_SPACE)
                    .totalSpace(FileConstant.INIT_SPACE)
                    .registerTime(LocalDateTime.now())
                    .lastLoginTime(LocalDateTime.now())
                    .build();
            log.info("准备注册新用户：{}", newUser);
            userMapper.insert(newUser);
            log.info("新用户注册成功，用户ID：{}", newUser.getId());
            // 执行登录操作
            session.setAttribute(String.valueOf(StatusConstant.DISABLE), newUser.getId());
            return (ModelAndView) doLogin(newUser, LoginType.QQ.name());
        } catch (Exception e) {
            log.error("QQ登录过程中出现异常：", e);
            throw new UserBusinessException(MessageConstant.LOGIN_FAILED);
        }
    }

    /**
     * 获取用户信息
     *
     * @return
     */
    @Override
    public UserLoginVO getUserInfo(HttpSession session) {
        Integer attribute = (Integer) session.getAttribute(String.valueOf(StatusConstant.DISABLE));
        User user = userMapper.selectById(attribute);
        return getUserLoginVO(user);
    }

    /**
     * 获取所有用户信息
     *
     * @param dto
     * @return
     */
    @Override
    public PageResult<AllUserVO> getAllUserInfo(AllUserDTO dto) {
        Page<AllUserVO> page = new Page<>(dto.getPageNum(), dto.getPageSize());
        page = userMapper.getAllUserInfo(page, dto);
        return new PageResult<>(page.getTotal(), page.getRecords());
    }

    /**
     * 更新用户状态
     *
     * @param id
     */
    @Override
    public void updateUserStatus(Integer id) {
        User oldUser = userMapper.selectById(id);
        Integer status;
        if (oldUser.getStatus().equals(StatusConstant.DISABLE)) {
            status = StatusConstant.ENABLE;
        } else {
            status = StatusConstant.DISABLE;
        }
        User user = User.builder()
                .id(id)
                .status(status)
                .build();
        // 更新用户状态
        userMapper.updateUser(user);
    }

    /**
     * 更新总用户空间
     *
     * @param dto
     */
    @Override
    public void updateUserTotalSpace(UpdateUserTotalSpaceDTO dto) {
        // 转换成MB
        Long space = dto.getSpace() * 1024 * 1024;
        User oldUser = userMapper.selectById(dto.getId());
        // 判断总空间是否小于已使用空间
        if (space + oldUser.getTotalSpace() < oldUser.getUsedSpace()) {
            throw new FileBusinessException(MessageConstant.TOTAL_SPACE_FAIL);
        }
        if (space + oldUser.getTotalSpace() < 0) {
            throw new FileBusinessException(MessageConstant.TOTAL_SPACE_FAIL);
        }
        space = space + oldUser.getTotalSpace();
        User user = User.builder()
                .id(dto.getId())
                .totalSpace(space)
                .build();
        // 更新用户总空间
        userMapper.updateById(user);
    }

    /**
     * 用户重命名
     *
     * @param name
     */
    @Override
    public void userRename(String name) {
        // 判断昵称是否已存在
        User user = findUserByNickName(name);
        if (user != null) {
            throw new UserBusinessException(MessageConstant.NICKNAME_EXISTS);
        }
        // 更新用户昵称
        User newUser = User.builder()
                .id(ThreadLocalUtil.getId())
                .nickName(name)
                .build();
        userMapper.updateById(newUser);
    }

    // 获取用户登录信息
    private UserLoginVO getUserLoginVO(User user) {
        // 生成token
        String token = jwtUtil.genToken(user);
        log.info("生成token成功");
        // 存放到redis中
        redisUtil.set(token, token, UserConstant.TOKEN_EXPIRE);
        log.info("token已存入redis");
        // 更新最后登录时间
        User newUser = User.builder()
                .id(user.getId())
                .lastLoginTime(LocalDateTime.now())
                .build();
        userMapper.updateUser(newUser);
        log.info("更新用户最后登录时间成功");
        // 封装VO并返回
        UserLoginVO userLoginVO = UserLoginVO.builder()
                .nickName(user.getNickName())
                .qqAvatar(user.getQqAvatar())
                .admin(user.getAdmin())
                .usedSpace(user.getUsedSpace())
                .totalSpace(user.getTotalSpace())
                .token(token)
                .build();
        log.info("登录操作完成，返回用户信息：{}", userLoginVO);
        return userLoginVO;
    }


    // 校验验证码
    private void checkCode(String key, String checkCode) {
        if (!Objects.equals(redisUtil.get(key), checkCode)) {
            throw new UserBusinessException(MessageConstant.CODE_ERROR);
        }
        // 移除已使用的验证
        redisUtil.deleteCache(key);
    }

    // 检验用户状态
    private Object doLogin(User user, String type) {
        // 检验用户是否被锁定
        if (user.getStatus().equals(StatusConstant.DISABLE)) {
            log.warn("用户账号已被锁定，用户ID：{}", user.getId());
            throw new UserBusinessException(MessageConstant.ACCOUNT_LOCKED);
        }
        if (Objects.equals(type, LoginType.QQ.name())) {
            return new ModelAndView("redirect:" + redirectUrl);
        }
        log.info("开始执行登录操作，用户信息：{}", user);

        // 生成token
        return getUserLoginVO(user);

    }
}
